import wormbane.config.commands as commands
import wormbane.config.keys as keys
import wormbane.config.material_colors as material_colors
from wormbane.geometry import Vector
from wormbane.util.dict_ import module_to_dict, invert_dict

import curses, curses.ascii, sys, random


_char_to_command = invert_dict(module_to_dict(commands))
_material_colors = module_to_dict(material_colors)
_hallucination_colors = ('_bold_blue', '_bold_cyan', '_bold_green',
                         '_bold_magenta', '_bold_red', '_bold_yellow')
           

class Ui:

    def __init__(self, stdscr):
        self.stdscr = stdscr
        self._colors = init_curses_colors()

    def update(self, game):
        game.level.field_of_view.update(game.hero.pos.x, game.hero.pos.y, 100)
        self._update_messages(game)
        self._update_cells(game)
        self._update_status(game)
        self._move_cursor_to_hero(game)
        self.stdscr.refresh()

    def _update_messages(self, game):
        height, width = self.stdscr.getmaxyx()
        self.stdscr.addstr(0, 0, ' ' * (width - 1))
        if game.messages:
            joined = ' '.join(game.messages)
            if len(joined) <= width - 1:
                self.stdscr.addstr(0, 0, joined)
            else:
                self.stdscr.clear()
                for y, message in zip(xrange(sys.maxint),
                                      game.messages[:height - 2]):
                    self.stdscr.addstr(y, 0, message[:width - 1])
                self.stdscr.addstr(height - 1, 0, 'Press any key...')
                self.stdscr.getch()
                del game.messages[:]
                self.stdscr.clear()

    def _update_cells(self, game):
        cells = game.level.cells
        level_width, level_height = game.level.size
        for level_x in xrange(level_width):
            for level_y in xrange(level_height):
                level_pos = Vector(level_x, level_y)
                ui_pos = self.level_pos_to_ui_pos(level_pos)
                cell = cells[level_pos]
                if cell.pos in game.level.field_of_view:
                    char = cell.char
                    if game.hero.hallucinating:
                        color = random.choice(_hallucination_colors)
                    else:
                        color = cell_color(cell)
                else:
                    char = game.level.memory[level_pos.y][level_pos.x]
                    color = '_black'
                attr = self._colors.get(color)
                try:
                    if attr is None:
                        self.stdscr.addch(ui_pos.y, ui_pos.x, char)
                    else:
                        self.stdscr.addch(ui_pos.y, ui_pos.x, char, attr)
                except curses.error, e:
                    pass

    def _update_status(self, game):
        status = '%s@Dungeons#%d' % (game.hero.name, game.level.level_num)
        if game.hero.hallucinating:
            status += '  Hallucinating'
        height, width = self.stdscr.getmaxyx()
        self.stdscr.addstr(height - 1, 0, ' ' * (width - 1))
        self.stdscr.addstr(height - 1, 0, status[:width - 1])

    def _move_cursor_to_hero(self, game):
        x, y = self.level_pos_to_ui_pos(game.hero.pos)
        try:
            self.stdscr.move(y, x)
        except curses.error, e:
            pass

    def read_command(self):
        while True:
            try:
                key = self.stdscr.getch()
                if key == curses.KEY_UP:
                    command = 'north'
                elif key == curses.KEY_LEFT:
                    command = 'west'
                elif key == curses.KEY_RIGHT:
                    command = 'east'
                elif key == curses.KEY_DOWN:
                    command = 'south'
                else:
                    command = chr(key)
            except ValueError:
                pass
            else:
                return _char_to_command.get(command, command)

    def level_pos_to_ui_pos(self, level_pos):
        ui_height = self.stdscr.getmaxyx()[0]
        return Vector(level_pos.x, ui_height - level_pos.y - 2)

def cell_color(cell):
    thing = cell.top_inv
    if thing.is_monster:
        return thing_color(thing.top_eq or thing)
    else:
        return thing_color(thing)

def thing_color(thing):
    return thing.color or _material_colors.get(thing.material)

def init_curses_colors():
    from wormbane.config import curses_colors

    curses.use_default_colors()
    for i in xrange(curses.COLOR_RED, curses.COLOR_WHITE + 1):
        curses.init_pair(i, i, -1)
    black = curses.color_pair(curses.COLOR_BLACK)
    red = curses.color_pair(curses.COLOR_RED)
    green = curses.color_pair(curses.COLOR_GREEN)
    yellow = curses.color_pair(curses.COLOR_YELLOW)
    blue = curses.color_pair(curses.COLOR_BLUE)
    magenta = curses.color_pair(curses.COLOR_MAGENTA)
    cyan = curses.color_pair(curses.COLOR_CYAN)
    white = curses.color_pair(curses.COLOR_WHITE)
    colors = {'black': black,
              'red': red,
              'green': green,
              'yellow': yellow,
              'blue': blue,
              'magenta': magenta,
              'cyan': cyan,
              'white': white}
    for key, value in colors.items():
        colors['bold_' + key] = curses.A_BOLD | value
    for key, value in colors.items():
        colors['_' + key] = value
    for key, value in module_to_dict(curses_colors).iteritems():
        colors[key] = colors[value]
    return colors
